#include "SPEmitterAnimTrail.h"
#include "SPD3D11RenderEnvironment.h"
#include "SPCamera.h"

//--------------------------------------------------------------------------
// SPEmitterAnimTrailRenderer
//--------------------------------------------------------------------------
namespace SP
{
	class SPEmitterAnimTrailRenderer : public SPD3DObject
	{
		friend void SPRenderAnimTrail( SPEmitterAnimTrail* pAT );
	protected:
		SPEmitterAnimTrailRenderer();
		~SPEmitterAnimTrailRenderer();

		HRESULT Create();
		void RenderAnimTrail( SPEmitterAnimTrail* pAT );

		struct CB
		{
			XMMATRIX mViewProj;	
			XMFLOAT4 vColor;
			FLOAT fUStride;
			FLOAT fUOffset;
			XMFLOAT2 fPadding;
		};

		ID3D11InputLayout* m_pVertexLayout;
		ID3D11VertexShader* m_pVertexShader;
		ID3D11HullShader* m_pHullShader[64];
		ID3D11DomainShader* m_pDomainShader;
		ID3D11PixelShader* m_pPixelShader;
		ID3D11PixelShader* m_pPSTexture;
		ID3D11Buffer* m_pCB;

		ID3D11SamplerState* m_pSampleState;
	};

	void SPRenderAnimTrail( SPEmitterAnimTrail* pAT )
	{
		static SPEmitterAnimTrailRenderer staticSPEATR;
		staticSPEATR.RenderAnimTrail( pAT );
	}

	SPEmitterAnimTrailRenderer::SPEmitterAnimTrailRenderer()
	{
		Create();
	}

	SPEmitterAnimTrailRenderer::~SPEmitterAnimTrailRenderer()
	{
		SAFE_RELEASE( m_pPixelShader );
		SAFE_RELEASE( m_pVertexShader );
		SAFE_RELEASE( m_pVertexLayout );
		for ( int i = 0; i < 64; i++ )
			SAFE_RELEASE( m_pHullShader[i] );
		SAFE_RELEASE( m_pDomainShader );
		SAFE_RELEASE( m_pPSTexture );
		SAFE_RELEASE( m_pCB );
	}

	HRESULT SPEmitterAnimTrailRenderer::Create()
	{
		HRESULT hr;

		// Create HullShaders
		for (int i = 0; i < 64; i++)
		{	
			ID3DBlob* pHSBlob = NULL;
			char strNum[5];
			sprintf_s( strNum, 5, "%d", i+1 );
			D3D_SHADER_MACRO defines[] = { { "TESS_AMOUNT", strNum }, { 0 } };
			V_RETURN( SPGetRenderEnvironment()->CompileShaderFromFile( L"hlsl\\AnimTrail.hlsl", defines, "HSMain", "hs_5_0", &pHSBlob ) );
			V_RETURN( m_pDevice->CreateHullShader( pHSBlob->GetBufferPointer(), pHSBlob->GetBufferSize(), NULL, &m_pHullShader[i] ) );
			SAFE_RELEASE( pHSBlob );
		}

		// Create the shaders
		ID3DBlob* pVSBlob = NULL;
		ID3DBlob* pPSBlob = NULL;
		ID3DBlob* pDSBlob = NULL;
		ID3DBlob* pPSTexBlob = NULL;

		V_RETURN( SPGetRenderEnvironment()->CompileShaderFromFile( L"hlsl\\AnimTrail.hlsl", NULL, "VSMain", "vs_5_0", &pVSBlob ) );
		V_RETURN( SPGetRenderEnvironment()->CompileShaderFromFile( L"hlsl\\AnimTrail.hlsl", NULL, "DSMain", "ds_5_0", &pDSBlob ) );
		V_RETURN( SPGetRenderEnvironment()->CompileShaderFromFile( L"hlsl\\AnimTrail.hlsl", NULL, "PSMain", "ps_5_0", &pPSBlob ) );
				
		D3D_SHADER_MACRO defines[] = { { "TEXTURE", 0 }, { 0 } };
		V_RETURN( SPGetRenderEnvironment()->CompileShaderFromFile( L"hlsl\\Beam.hlsl", defines, "PSMain", "ps_5_0", &pPSTexBlob ) );

		V_RETURN( m_pDevice->CreateVertexShader( pVSBlob->GetBufferPointer(), pVSBlob->GetBufferSize(), NULL, &m_pVertexShader ) );
		V_RETURN( m_pDevice->CreateDomainShader( pDSBlob->GetBufferPointer(), pDSBlob->GetBufferSize(), NULL, &m_pDomainShader ) );
		V_RETURN( m_pDevice->CreatePixelShader( pPSBlob->GetBufferPointer(), pPSBlob->GetBufferSize(), NULL, &m_pPixelShader ) );
		
		V_RETURN( m_pDevice->CreatePixelShader( pPSTexBlob->GetBufferPointer(), pPSTexBlob->GetBufferSize(), NULL, &m_pPSTexture ) );

		// Define the input layout
		D3D11_INPUT_ELEMENT_DESC layout[] =
		{
			{ "CtrlPos", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "PrePos", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "PostPos", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
		};
		UINT numElements = ARRAYSIZE( layout );
		// Create the input layout
		V_RETURN(m_pDevice->CreateInputLayout( layout, numElements, pVSBlob->GetBufferPointer(),
			pVSBlob->GetBufferSize(), &m_pVertexLayout ) );

		SAFE_RELEASE( pVSBlob );
		SAFE_RELEASE( pPSBlob );
		SAFE_RELEASE( pDSBlob );
		SAFE_RELEASE( pPSTexBlob );

		// Create the constant buffers 
		D3D11_BUFFER_DESC Desc;
		Desc.Usage = D3D11_USAGE_DYNAMIC;
		Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		Desc.MiscFlags = 0;
		Desc.ByteWidth = sizeof( CB );
		V_RETURN( m_pDevice->CreateBuffer( &Desc, NULL, &m_pCB ) );

		// CreateSampleState
		D3D11_SAMPLER_DESC SamplerDesc;
		ZeroMemory(  &SamplerDesc, sizeof( SamplerDesc ) );
		SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		V_RETURN(  m_pDevice->CreateSamplerState( &SamplerDesc, &m_pSampleState ) );

		return S_OK;
	}

	void SPEmitterAnimTrailRenderer::RenderAnimTrail( SPEmitterAnimTrail* pAT )
	{
		SPCamera* pCamera = SPGetCamera();
		XMMATRIX mViewProj = pCamera->GetViewProjMtx();

		// Constant buffer
		D3D11_MAPPED_SUBRESOURCE MappedResource;
		m_pImmediateContext->Map( m_pCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource );
		CB* pCB = ( CB* )MappedResource.pData;
		pCB->mViewProj = XMMatrixTranspose( mViewProj );
		XMStoreFloat4( &pCB->vColor, pAT->GetColourVector() );
		pCB->fUStride = 1.0f / ( pAT->m_nNumControlLine - 1 );
		pCB->fUOffset = ( 1.0f - pAT->m_fNewControlLineRamain / pAT->m_fAnimTrailDuration ) * pCB->fUStride;
		m_pImmediateContext->Unmap( m_pCB, 0 );

		m_pImmediateContext->VSSetConstantBuffers( 0, 1, &m_pCB );
		m_pImmediateContext->PSSetConstantBuffers( 0, 1, &m_pCB );

		// Set shaders
		m_pImmediateContext->VSSetShader( m_pVertexShader, NULL, 0 );

		m_pImmediateContext->DSSetShader( m_pDomainShader, NULL, 0 );
		m_pImmediateContext->GSSetShader( NULL, NULL, 0 );

		UINT iHS = pAT->m_nTessellationAmount - 1;
		iHS = iHS > 0 ? iHS : 0;
		iHS = iHS < 64 ? iHS : 64;
		m_pImmediateContext->HSSetShader( m_pHullShader[iHS], NULL, 0 );

		if ( pAT->m_pTexSRV )
		{
			m_pImmediateContext->PSSetShader( m_pPSTexture, NULL, 0 );
			m_pImmediateContext->PSSetShaderResources(  0, 1, &pAT->m_pTexSRV );
		}
		else
		{
			m_pImmediateContext->PSSetShader( m_pPixelShader, NULL, 0 );
		}

		// VertexBuffer & IndexBuffer
		m_pImmediateContext->IASetInputLayout(m_pVertexLayout);
		UINT Strides[1];
		UINT Offsets[1];
		ID3D11Buffer* pVB[1];
		pVB[0] = pAT->m_pVertexBuffer;
		Strides[0] = sizeof( SPEmitterAnimTrail::VERTEX );
		Offsets[0] = 0;
		m_pImmediateContext->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );

		m_pImmediateContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST );
		
		// SetSampleState
		m_pImmediateContext->PSSetSamplers(  0, 1, &m_pSampleState );
		// SetRasterizerState
		m_pImmediateContext->RSSetState( pAT->GetRasterizerState() );
		// SetBlendState
		float vBlendFactor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
		m_pImmediateContext->OMSetBlendState( pAT->GetBlendState(), vBlendFactor, 0xFFFFFFFF );
		// SetDepthStencilState
		m_pImmediateContext->OMSetDepthStencilState( pAT->GetDepthStencilState(), 0 );

		// Draw 
		m_pImmediateContext->Draw( pAT->m_nNumControlLine * 2 - 2, 0 ) ;

		m_pImmediateContext->HSSetShader( NULL, NULL, 0 );
		m_pImmediateContext->DSSetShader( NULL, NULL, 0 );

		ID3D11Buffer* ppCBNULL[1] = { NULL };
		m_pImmediateContext->VSSetConstantBuffers( 0, 1, ppCBNULL );
		m_pImmediateContext->PSSetConstantBuffers( 0, 1, ppCBNULL );
	}
}

//--------------------------------------------------------------------------
// SPEmitterAnimTrail
//--------------------------------------------------------------------------
namespace SP
{
	void SPEmitterAnimTrail::SetZero()
	{
		m_bAbsHead = false;

		m_pVertices = NULL;
		m_pVerticesCopy = NULL;

		m_vLeftHead = XMFLOAT3( 0.0f, 0.0f, 0.0f );
		m_vRightHead = XMFLOAT3( 0.0f, 0.0f, 0.0f );

		m_fNewControlLineRamain = 0.0f;
		m_fAnimTrailDuration = 1.0f;
		m_nTessellationAmount = 10;

		m_pVertexBuffer = NULL;

		m_pTexSRV = NULL;
		m_pTex = NULL;
	}

	SPEmitterAnimTrail::SPEmitterAnimTrail()
	{
		SetZero();

		Create( 10 );
	}

	SPEmitterAnimTrail::SPEmitterAnimTrail( SPEmitterAnimTrail* archetpye )
	{
		SetZero();

		m_nTessellationAmount = archetpye->m_nTessellationAmount;
		m_fAnimTrailDuration = archetpye->m_fAnimTrailDuration;
		m_bAbsHead = m_bAbsHead;

		Create( archetpye->m_nNumControlLine );
	}

	void SPEmitterAnimTrail::Create(  SPEmitterAnimTrail* archetype )
	{
		SetZero();

		m_nTessellationAmount = archetype->m_nTessellationAmount;
		m_fAnimTrailDuration = archetype->m_fAnimTrailDuration;
		m_bAbsHead = m_bAbsHead;

		Create( archetype->m_nNumControlLine );	
	}

	SPEmitterAnimTrail::~SPEmitterAnimTrail()
	{
		Release();
	}

	void SPEmitterAnimTrail::Release()
	{
		SAFE_DELETE( m_pVertices );
		SAFE_DELETE( m_pVerticesCopy );

		SAFE_RELEASE( m_pVertexBuffer );
		SAFE_RELEASE( m_pTex );
		SAFE_RELEASE( m_pTexSRV );
	}

	HRESULT SPEmitterAnimTrail::Create( UINT nNumControlLine )
	{
		HRESULT hr = S_OK;

		if ( m_nNumControlLine == nNumControlLine )
		{
			return hr;
		}

		Release();

		m_nNumControlLine = nNumControlLine;

		m_pVertices = new VERTEX[m_nNumControlLine * 2];
		m_pVerticesCopy = new VERTEX[m_nNumControlLine * 2];

		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( m_nNumControlLine * 2, sizeof( VERTEX ), NULL, &m_pVertexBuffer ) );
 
		Initialize();

		return hr;
	}
	
	void SPEmitterAnimTrail::Initialize()
	{
		SPEmitter::Initialize();

		m_fNewControlLineRamain = 0.0f;

		VERTEX vL,vR;
		vL.vCtrlPos = vL.vPostPos = vL.vPrePos = m_vLeftHead;
		vR.vCtrlPos = vR.vPostPos = vR.vPrePos = m_vRightHead;
		for ( UINT i = 0; i < m_nNumControlLine; i++ )
		{
			m_pVertices[i*2] = vL;
			m_pVertices[i*2+1] = vR;
		}
		this->m_pImmediateContext->UpdateSubresource( m_pVertexBuffer, 0, 0, m_pVertices, 0, 0 );
	}

	void SPEmitterAnimTrail::Render()
	{

		SPRenderAnimTrail( this );
	}

	namespace
	{
		XMFLOAT3 Float3Lerp( const XMFLOAT3& v1, const XMFLOAT3& v2, FLOAT t )
		{
			XMFLOAT3 v;
			FLOAT invt = 1.0f - t;

			v.x = invt * v1.x + v2.x * t;
			v.y = invt * v1.y + v2.y * t;
			v.z = invt * v1.z + v2.z * t;

			return v;
		}
	}

	void SPEmitterAnimTrail::Advance( float fElapsedTime )
	{
		SPEmitter::Advance( fElapsedTime );

		m_fNewControlLineRamain -= fElapsedTime;
		if ( m_fNewControlLineRamain <= 0.0f )
		{
			m_fNewControlLineRamain = m_fAnimTrailDuration;

			UINT sizeofMemory = sizeof(VERTEX) * (m_nNumControlLine - 1) * 2;
			CopyMemory( m_pVerticesCopy, m_pVertices, sizeofMemory );
			CopyMemory( m_pVertices + 2, m_pVerticesCopy, sizeofMemory );
			m_vLeftEnd = m_pVertices[m_nNumControlLine*2-2];
			m_vRightEnd = m_pVertices[m_nNumControlLine*2-1];
		}

		XMMATRIX mTransform = GetTransformMatrix();

		// Trail Begin
		VERTEX& vLeft = m_pVertices[0];
		VERTEX& vRight = m_pVertices[1];

		XMVECTOR VL = XMLoadFloat3( &m_vLeftHead );
		XMVECTOR VR = XMLoadFloat3( &m_vRightHead );
		
		VL = XMVector3Transform( VL, mTransform );
		VR = XMVector3Transform( VR, mTransform );

		XMStoreFloat3( &vLeft.vCtrlPos, VL );
		XMStoreFloat3( &vRight.vCtrlPos, VR );

		XMStoreFloat3( &vLeft.vPrePos, VL );
		XMStoreFloat3( &vRight.vPrePos, VR );

		vLeft.vPostPos = m_pVertices[2].vPrePos = Float3Lerp( vLeft.vCtrlPos, m_pVertices[2].vCtrlPos , 0.5f );
		vRight.vPostPos = m_pVertices[3].vPrePos = Float3Lerp( vRight.vCtrlPos, m_pVertices[3].vCtrlPos, 0.5f );

		// Trail End
		
		VERTEX& vLeftEnd = m_pVertices[m_nNumControlLine*2-2];
		VERTEX& vRightEnd = m_pVertices[m_nNumControlLine*2-1];
		VERTEX& vLeftEndNext = m_pVertices[m_nNumControlLine*2-4];
		VERTEX& vRightEndNext = m_pVertices[m_nNumControlLine*2-3];
		
		FLOAT t = m_fNewControlLineRamain / m_fAnimTrailDuration;

		vLeftEndNext.vPostPos = Float3Lerp( vLeftEndNext.vCtrlPos, vLeftEnd.vCtrlPos, t );
		vRightEndNext.vPostPos = Float3Lerp( vRightEndNext.vCtrlPos, vRightEnd.vCtrlPos, t );

		this->m_pImmediateContext->UpdateSubresource( m_pVertexBuffer, 0, 0, m_pVertices, 0, 0 );
	}

	void SPEmitterAnimTrail::ClearModule()
	{
		SPEmitter::ClearModule();
	}

	void SPEmitterAnimTrail::CreateFileBuffer( SPFileBuffer* pFB )
	{
		HRESULT hr;
		ID3DBlob* pBlob = NULL;

		SPFileBuffer* pEmitterFB = new SPFileBuffer();
		SPEmitter::CreateFileBuffer( pEmitterFB );

		int numbyte = pEmitterFB->nNumByte;
		numbyte += sizeof( EmitterAnimTrailFileHead );

		EmitterAnimTrailFileHead head;
		ZeroMemory( &head, sizeof( EmitterAnimTrailFileHead ) );

		head.bAbsHead = m_bAbsHead;
		head.fAnimTrailDuration = m_fAnimTrailDuration;
		head.nNumControlLine = m_nNumControlLine;
		head.nTessellationAmount = m_nTessellationAmount;
		head.vLeftHead = m_vLeftHead;
		head.vRightHead = m_vRightHead;
		head.bTexture = false;		
		if ( m_pTex )
		{
			V( D3DX11SaveTextureToMemory( SPGetD3D11DeviceContext(), m_pTex, D3DX11_IFF_DDS, &pBlob, NULL ) );
			numbyte += pBlob->GetBufferSize();			
			head.bTexture = true;		
			head.nTextureBufferSize = pBlob->GetBufferSize();
		}

		pFB->CreateBuffer( numbyte );

		BYTE* pBuf = pFB->pFileBuffer;

		CopyMemory( pBuf, pEmitterFB->pFileBuffer, pEmitterFB->nNumByte );
		pBuf += pEmitterFB->nNumByte;

		CopyMemory( pBuf, &head, sizeof( EmitterAnimTrailFileHead ) );
		pBuf += sizeof( EmitterAnimTrailFileHead );
		if ( head.bTexture )
		{
			CopyMemory( pBuf, pBlob->GetBufferPointer(), pBlob->GetBufferSize() );
			pBuf += pBlob->GetBufferSize();
		}

		SAFE_DELETE( pEmitterFB );

		SAFE_RELEASE( pBlob );
	}

	void SPEmitterAnimTrail::CreateFromFileBuffer( BYTE* pBufPtr )
	{
		HRESULT hr;

		SPEmitter::CreateFromFileBuffer( pBufPtr );

		UINT offset = ( (EmitterFileHead*)pBufPtr )->nByteCount;
		BYTE* pBuf = pBufPtr + offset;
		EmitterAnimTrailFileHead* pHead = (EmitterAnimTrailFileHead*)pBuf;

		m_nTessellationAmount = pHead->nTessellationAmount;
		m_fAnimTrailDuration = pHead->fAnimTrailDuration;
		m_bAbsHead = pHead->bAbsHead;
		m_vLeftHead = pHead->vLeftHead;
		m_vRightHead = pHead->vRightHead;
		Create( pHead->nNumControlLine );

		if ( pHead->bTexture )
		{		
			pBuf += sizeof( EmitterAnimTrailFileHead );
			V( D3DX11CreateTextureFromMemory( SPGetD3D11Device(), pBuf, pHead->nTextureBufferSize, NULL, NULL, &m_pTex, NULL) );
			V( D3DX11CreateShaderResourceViewFromMemory( SPGetD3D11Device(), pBuf, pHead->nTextureBufferSize, NULL, NULL, &m_pTexSRV, NULL) );
		}
	}

	bool SPEmitterAnimTrail::SetSPModule( SPModule* pModule )
	{
		return SPEmitter::SetSPModule( pModule );
	}

	HRESULT SPEmitterAnimTrail::CreateTextureFromFile( const wchar_t* file )
	{
		HRESULT hr = S_OK;

		V_RETURN( D3DX11CreateTextureFromFile(  m_pDevice, file, NULL, NULL, &m_pTex, NULL ) );
		V_RETURN( D3DX11CreateShaderResourceViewFromFile(  m_pDevice, file, NULL, NULL, &m_pTexSRV, NULL ) );

		return hr;
	}
}